package scatter.common.rest.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import scatter.common.rest.concurrency.ratelimit.RateLimitInterceptor;
import scatter.common.rest.dataconstraint.DataConstraintService;
import scatter.common.rest.dataconstraint.DefalutDataConstraintServiceImpl;
import scatter.common.rest.interceptor.GlobalInterceptor;
import scatter.common.rest.monitor.MicromterMonitorListenerImpl;
import scatter.common.rest.security.InnerPathConfig;
import scatter.common.rest.security.LoginUserArgumentResolver;
import scatter.common.rest.serializer.CustomJacksonAnnotationIntrospector;
import scatter.common.rest.service.MetricsAndSlowSqlMybatisInterceptor;
import scatter.common.rest.service.mybatisplusfill.MpMetaObjectHandler;
import scatter.common.rest.tools.CommonSingletonTool;
import springfox.documentation.oas.annotations.EnableOpenApi;

import java.util.List;

/**
 * <p>
 * 通用rest配置
 * CommonWebSecurityConfig.class 自行引用，因为在不需要的情况下会报错
 * </p>
 *
 * @author yangwei
 * @since 2020-11-05 23:43
 */
@EnableOpenApi
@Import({CommonRedisConfig.class,CommonComponentScanConfig.class,CommonFilterConfig.class,CommonSwaggerConfig.class,DateConverterConfig.class,CommonExecutorsConfig.class/*, CommonWebSecurityConfig.class*/})

public class CommonRestConfig implements WebMvcConfigurer {

    @Autowired
    private RateLimitInterceptor rateLimitInterceptor;
    @Autowired
    private GlobalInterceptor globalInterceptor;


    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(CommonSingletonTool.objectMapperForHttpSingleton());
        converters.add(0, converter);
    }

    @Bean
    public CustomJacksonAnnotationIntrospector serializerAnnotationIntrospector(){
        return new CustomJacksonAnnotationIntrospector();
    }

    /**
     * mybatisplus
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        mybatisPlusInterceptor.addInnerInterceptor( new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

    /**
     * 监控通知 mybatis 拦截器
     * @return
     */
    @Bean
    public MetricsAndSlowSqlMybatisInterceptor MetricsAndSlowSqlMybatisPlusInterceptor(){
        return new MetricsAndSlowSqlMybatisInterceptor();
    }


    /**
     * 自定义 mybatisplus 填充处理
     * @return
     */
    @Bean
    public MpMetaObjectHandler mpMetaObjectHandler(){
        return new MpMetaObjectHandler();
    }

    /**
     * 代理生成器
     * @return
     */
    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(globalInterceptor).addPathPatterns("/**");
        registry.addInterceptor(rateLimitInterceptor).addPathPatterns("/**");
    }

    /**
     * 注入当前登录用户
     * @param resolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new LoginUserArgumentResolver());
    }
    /**
     * 提供一个默认的数据范围服务
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public DataConstraintService dataConstraintService() {
        return new DefalutDataConstraintServiceImpl();
    }

    /**
     * 禁用内部调用
     * @return
     */
    @Bean
    @ConditionalOnProperty(prefix = "scatter.inner-path",name = "disable",havingValue = "true",matchIfMissing = true)
    public InnerPathConfig innerPathConfig(){
        return new InnerPathConfig();
    }

    /**
     * http客户端
     * @return
     */
    @Bean
    @ConditionalOnProperty(prefix = "scatter.rest-template",name = "disable",havingValue = "true",matchIfMissing = true)
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }


    /**
     * micrometer监控工具实现
     * @return
     */
    @Bean
    @ConditionalOnMissingBean
    public MicromterMonitorListenerImpl micromterMonitorListener(){
        return new MicromterMonitorListenerImpl();
    }


    /**
     * 添加 application tag
     * 也可以在配置文件中添加如下：是一样的效果
     * management:
     *   metrics:
     *     tags:
     *       application: ${spring.application.name}
     *
     */
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(@Value("${spring.application.name}") String applicationName) {
        return (registry) -> registry.config().commonTags("application",applicationName);
    }
}
